package bold.module.netty.nio;

import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.*;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;

/**
 * http://www.cnblogs.com/coderjun/p/7100423.html
 * nio 区别
 * 1、面向缓冲区编 可读可写
 * 2、网络 非堵塞
 * 3、网络 选择器事件
 */
public class Nio {
    String name = "/Users/lkl/work/public/bold/module/src/main/java/bold/module/netty/nio/nio.text";

    @Test
    public void nio() throws IOException {
        Path path = Paths.get(name);

        if (!Files.exists(path))
            Files.createFile(path);
    }

    @Test
    public void write_read() throws IOException {
        FileChannel channel = FileChannel.open(Paths.get(name),
                StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
        channel.position(channel.size());
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //数据写入Buffer
        byteBuffer.put("法规宣传费是从VB回济南开幕上环保局那款美丽乡村VB纳米颗粒".getBytes(StandardCharsets.UTF_8));
        byteBuffer.flip(); // 设置 limit 为 position 的值，然后 position 置为0。反转成读模式

        if (byteBuffer.hasRemaining()) //写入 channel
            channel.write(byteBuffer);
        byteBuffer.clear();


        // channel.truncate(0);//清空所有
        channel.position(0);
        while (channel.read(byteBuffer) != -1) {
            byteBuffer.flip();
            byte[] array = byteBuffer.array();
            System.out.println(new String(array, 0, byteBuffer.limit()));
            byteBuffer.clear();
        }
        channel.close();
    }

    @Test
    public void read() throws IOException {
        FileChannel open = FileChannel.open(Paths.get(name), StandardOpenOption.READ);
        CharBuffer charBuffer = CharBuffer.allocate(10);
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
        StringBuffer sb = new StringBuffer();
        while (open.read(byteBuffer) != -1) {
            byteBuffer.flip();
            decoder.decode(byteBuffer, charBuffer, false);
            charBuffer.flip();
            sb.append(new String(charBuffer.array(), 0, charBuffer.limit()));
            charBuffer.clear();
            byteBuffer.compact();//核心 不丢数据
        }
        System.out.println(sb.toString());
        open.close();
    }

    @Test
    public void service() throws IOException {
        // 1、创建一个selector
        Selector selector = Selector.open();

        // 2、创建
        ServerSocketChannel
                .open()
                .bind((new InetSocketAddress(9999)))
                .configureBlocking(false)                   // 设置非阻塞
                .register(selector, SelectionKey.OP_ACCEPT);// 注册到selector（监听其ACCEPT事件）

        // 创建一个缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(100);

        while (true) {
            selector.select(); //阻塞，直到有监听的事件发生
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

            // 通过迭代器依次访问select出来的Channel事件
            while (keyIter.hasNext()) {
                SelectionKey key = keyIter.next();

                if (key.isAcceptable()) { // 有连接可以接受
                    SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
                    channel.configureBlocking(false);
                    channel.register(selector, SelectionKey.OP_READ);

                    System.out.println("与【" + channel.getRemoteAddress() + "】建立了连接！");
                } else if (key.isReadable()) { // 有数据可以读取
                    buffer.clear();

                    // 读取到流末尾说明TCP连接已断开，
                    // 因此需要关闭通道或者取消监听READ事件
                    // 否则会无限循环
                    if (((SocketChannel) key.channel()).read(buffer) == -1) {
                        key.channel().close();
                        continue;
                    }

                    // 按字节遍历数据
                    buffer.flip();
                    while (buffer.hasRemaining()) {
                        byte b = buffer.get();

                        if (b == 0) { // 客户端消息末尾的\0
                            System.out.println();

                            // 响应客户端
                            buffer.clear();
                            buffer.put("Hello, Client!\0".getBytes());
                            buffer.flip();
                            while (buffer.hasRemaining()) {
                                ((SocketChannel) key.channel()).write(buffer);
                            }
                        } else {
                            System.out.print((char) b);
                        }
                    }
                }

                // 已经处理的事件一定要手动移除
                keyIter.remove();
            }
        }
    }

    @Test
    public void client() throws IOException {
        Socket socket = new Socket("localhost", 9999);
        InputStream is = socket.getInputStream();
        OutputStream out = socket.getOutputStream();

        // 先向服务端发送数据
        out.write("Hello, Server!\0".getBytes());

        // 读取服务端发来的数据
        int b;
        while ((b = is.read()) != 0) {
            System.out.print((char) b);
        }
        System.out.println();

        socket.close();
    }

}
